﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.NetworkAnalyst;
using ESRI.ArcGIS.Geometry;
using Entity;
using Bussiness;

namespace Utilities
{
    public class ClosestFacilityLayer
    {
        private const String FGDB_WORKSPACE = @"E:\VirtualCampus\ArcGlobeData\ZhiMingBuilding\Campus3D\BuildingWays.gdb";
        //private const String FGDB_WORKSPACE = @"\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb";
        private const String INPUT_INCIDENTS_FC = "TClassRoom";
        private const String INPUT_FACILITY_FC = "TExistPois";
        private const String INPUT_BARRIER_FC = "TPointBarrier";
        private const String INPUT_NAME_FIELD = "Name";
        private const String FEATURE_DATASET = "Transportation";
        private const String NETWORK_DATASET = "Transportation_ND";

        private string _CostFieldName="Time";        
        private int _CrCode = 0;
        private IFeatureClass _RouteFC=null;
        private int _CourseID;
        


 

        /// <summary>
        /// Create the analysis layer, load the locations, solve the analysis, and write to disk
        /// </summary>
        public void SolveRoute(int crCode,int GroupID,int courseID,object barrierCode)
        {
            this._CrCode = crCode;
            this._CourseID = courseID;
            // Open the feature workspace, input feature class, and network dataset
            // As Workspace Factories are Singleton objects, they must be instantiated with the Activator
            IWorkspaceFactory workspaceFactory = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory")) as IWorkspaceFactory;
            IFeatureWorkspace featureWorkspace = workspaceFactory.OpenFromFile(FGDB_WORKSPACE, 0) as IFeatureWorkspace;
            IFeatureClass inputIncidentsFClass = featureWorkspace.OpenFeatureClass(INPUT_INCIDENTS_FC);
            IFeatureClass inputFacilityFClass = featureWorkspace.OpenFeatureClass(INPUT_FACILITY_FC);
            IFeatureClass inputBarrierFClass = featureWorkspace.OpenFeatureClass(INPUT_BARRIER_FC);

            // Obtain the dataset container from the workspace
            ESRI.ArcGIS.Geodatabase.IFeatureDataset featureDataset = featureWorkspace.OpenFeatureDataset(FEATURE_DATASET);
            var featureDatasetExtensionContainer = featureDataset as ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtensionContainer;
            ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtension featureDatasetExtension = featureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset);
            var datasetContainer3 = featureDatasetExtension as ESRI.ArcGIS.Geodatabase.IDatasetContainer3;

            // Use the container to open the network dataset.
            ESRI.ArcGIS.Geodatabase.IDataset dataset = datasetContainer3.get_DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset, NETWORK_DATASET);
            INetworkDataset networkDataset = dataset as INetworkDataset;

            // Create the Closest Facility NALayer
            INALayer naLayer = CreateClosestFacilitLayer("Facility", networkDataset);
            INAContext naContext = naLayer.Context;
            INAClass facilityNAClass = naContext.NAClasses.get_ItemByName("Facilities") as INAClass;
            INAClass incidentsNAClass = naContext.NAClasses.get_ItemByName("Incidents") as INAClass;
            INAClass pointBarrierNAClass = naContext.NAClasses.get_ItemByName("Barriers") as INAClass;      
            IFeatureClass routesFC = naContext.NAClasses.get_ItemByName("CFRoutes") as IFeatureClass;
            this._RouteFC = routesFC;

            // Load the Stops
            INAClassFieldMap naClassFieldMap = new NAClassFieldMapClass();
            naClassFieldMap.set_MappedField("Name", INPUT_NAME_FIELD);

            INAClassLoader naFacilityLoader = new NAClassLoaderClass();
            naFacilityLoader.Locator = naContext.Locator;
            naFacilityLoader.NAClass = facilityNAClass;
            naFacilityLoader.FieldMap = naClassFieldMap;

            INAClassLoader naIncidentsLoader = new NAClassLoaderClass();
            naIncidentsLoader.Locator = naContext.Locator;
            naIncidentsLoader.NAClass = incidentsNAClass;
            naIncidentsLoader.FieldMap = naClassFieldMap;

            INAClassLoader naPointBarrierLoader = new NAClassLoaderClass();
            naPointBarrierLoader.Locator = naContext.Locator;
            naPointBarrierLoader.NAClass = pointBarrierNAClass;

            // Avoid loading network locations onto non-traversable portions of elements
            //INALocator3 locator = naContext.Locator as INALocator3;
            //locator.ExcludeRestrictedElements = true;
            //locator.CacheRestrictedElements(naContext);

            int rowsInCursor = 0;
            int rowsLocated = 0;
            naFacilityLoader.Load(inputFacilityFClass.Search(new QueryFilterClass(), false) as ICursor, new CancelTrackerClass(), ref rowsInCursor, ref rowsLocated);

            IQueryFilter incidentsFilter = new QueryFilterClass();
            incidentsFilter.WhereClause = "CRCode=" + crCode.ToString();
            

            int rowsIncidentsCursor = 0;
            int rowsIncidentsLocated = 0;
            naIncidentsLoader.Load(inputIncidentsFClass.Search(incidentsFilter, false) as ICursor, new CancelTrackerClass(), ref rowsIncidentsCursor, ref rowsIncidentsLocated);

            if (barrierCode != null)
            {
                IQueryFilter pointBarrierFilter = new QueryFilterClass();
                pointBarrierFilter.WhereClause = "CRCode=" + barrierCode.ToString();

                int rowsPointBarrerCursor = 0;
                int rowsPointBarrerLocated = 0;
                naPointBarrierLoader.Load(inputBarrierFClass.Search(pointBarrierFilter, false) as ICursor, new CancelTrackerClass(), ref rowsPointBarrerCursor, ref rowsPointBarrerLocated);
            }
            //Message all of the network analysis agents that the analysis context has changed
            ((INAContextEdit)naContext).ContextChanged();

            //Solve
            INASolver naSolver = naContext.Solver;
            naSolver.Solve(naContext, new GPMessagesClass(), new CancelTrackerClass());

            //Save the layer to disk
            //EsriUtility.SaveLayerToDisk(naLayer as ILayer, @"E:\data\output\Route6.lyr");

            insertNodes(GroupID);
        }

        /// <summary>
        /// Create a new network analysis layer and set some solver settings
        /// </summary>
        private INALayer CreateClosestFacilitLayer(String layerName, INetworkDataset networkDataset)
        {
            //INARouteSolver naRouteSolver = new NARouteSolverClass();
            INAClosestFacilitySolver naClosestFacilitySolver = new NAClosestFacilitySolverClass();
            INASolverSettings naSolverSettings = naClosestFacilitySolver as INASolverSettings;
            naSolverSettings.ImpedanceAttributeName = this._CostFieldName;
            INASolver naSolver = naClosestFacilitySolver as INASolver;

            //Get the NetworkDataset's Data Element
            IDatasetComponent datasetComponent = networkDataset as IDatasetComponent;
            IDENetworkDataset deNetworkDataset = datasetComponent.DataElement as IDENetworkDataset;

            //Create the NAContext and bind to it
            INAContext naContext;
            naContext = naSolver.CreateContext(deNetworkDataset, layerName);
            INAContextEdit naContextEdit = naContext as INAContextEdit;
            naContextEdit.Bind(networkDataset, new GPMessagesClass());

            //Create the NALayer
            INALayer naLayer;
            naLayer = naSolver.CreateLayer(naContext);
            (naLayer as ILayer).Name = layerName;

            //Set properties on the route solver interface
            naClosestFacilitySolver.DefaultTargetFacilityCount = 1;
            naClosestFacilitySolver.CreateTraversalResult = true;
            naClosestFacilitySolver.OutputLines = esriNAOutputLineType.esriNAOutputLineTrueShapeWithMeasure;

            //Set some properties on the general INASolverSettings interface
            ////IStringArray restrictions = naSolverSettings.RestrictionAttributeNames;
            ////restrictions.Add("Oneway");
            ////naSolverSettings.RestrictionAttributeNames = restrictions;

            // Update the context based on the changes made to the solver settings
            naSolver.UpdateContext(naContext, deNetworkDataset, new GPMessagesClass());

            //Return the layer
            return naLayer;
        }

        private bool insertNodes(int GroupID)
        {
            IFeatureCursor pFeatureCursor;
            IFeature lineFeature;
            IPointCollection pointCollection;
            string lons = string.Empty;
            string lats = string.Empty;
            string alts = string.Empty;

            pFeatureCursor = this._RouteFC.Search(null, false);
            lineFeature = pFeatureCursor.NextFeature();
            pointCollection = lineFeature.Shape as IPointCollection;

            for (int i = 0; i < pointCollection.PointCount; i++)
            {
                IPoint point = new PointClass();
                IZAware zAware = point as IZAware;
                zAware.ZAware = true;

                pointCollection.QueryPoint(i, point);
                IPoint poiPrj = EsriUtility.PCS2GCS(point.X, point.Y);
                ERouteNode node = new ERouteNode(poiPrj.X, poiPrj.Y, point.Z);
                lons = lons + poiPrj.X + ",";
                lats = lats + poiPrj.Y + ",";
                alts = alts + point.Z + ",";
            }
            BestRoutesManager br = new BestRoutesManager();
            return br.BestRouteAdd(this._CrCode, GroupID, lons, lats, alts, this._CostFieldName, GetTotalCost(), GetFacilityId(),this._CourseID);           
        }
        private double GetTotalCost()
        {
            IFeature row = this._RouteFC.GetFeature(1);
            int fieldCount=row.Fields.FieldCount;
            double totalCost =Double.Parse(row.get_Value(fieldCount - 1).ToString());
            return totalCost;
        }
        private int GetFacilityId()
        {
            IFeature row = this._RouteFC.GetFeature(1);
            int fieldCount = row.Fields.FieldCount;
            string[] str= row.get_Value(4).ToString().Split('-');            
            int facilityID = Int32.Parse(str[1]);
            return facilityID;
        }
      
    }
}
